package com.ly.nethub.state

import android.content.Context
import android.net.ConnectivityManager
import android.net.Network
import android.net.NetworkCapabilities
import android.net.NetworkRequest
import com.ly.nethub.utils.LogUtils
import java.util.concurrent.CopyOnWriteArrayList

enum class NetworkType {
    Wifi, Cellular, Other
}

interface INetworkStateChangedObserver {
    fun onNetworkAvailable()

    fun onNetworkUnavailable()
}

object NetworkManager : ConnectivityManager.NetworkCallback() {

    private const val TAG = "NetworkManager"

    private val mObservers = CopyOnWriteArrayList<INetworkStateChangedObserver>()

    private var mNetworkType = NetworkType.Cellular

    private var mIsCallbackRegistered = false

    override fun onAvailable(network: Network) {
        super.onAvailable(network)
        LogUtils.d("$TAG onAvailable network:$network")
        notifyObserver(true)
    }

    override fun onLost(network: Network) {
        super.onLost(network)
        LogUtils.d("$TAG onLost network:$network")
        notifyObserver(false)
    }

    override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
        super.onCapabilitiesChanged(network, networkCapabilities)
        LogUtils.d(
            "$TAG onCapabilitiesChanged network:$network , capabilities:${
                networkCapabilities.hasCapability(
                    NetworkCapabilities.NET_CAPABILITY_VALIDATED
                )
            }"
        )
        if (networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)) {
            if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
                updateNetworkType(NetworkType.Wifi)
            } else if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
                updateNetworkType(NetworkType.Cellular)
            } else {
                updateNetworkType(NetworkType.Other)
            }
        }
    }

    override fun onUnavailable() {
        super.onUnavailable()
        LogUtils.d("$TAG onUnavailable")
    }

    private fun updateNetworkType(type: NetworkType) {
        if (mNetworkType == type) {
            return
        }
        mNetworkType = type
        LogUtils.d("$TAG updateNetworkType $type")
    }

    fun registerObserver(context: Context, observer: INetworkStateChangedObserver) {
        if (mObservers.contains(observer)) {
            return
        }
        val request = NetworkRequest.Builder().build()
        if (!mIsCallbackRegistered || mObservers.isEmpty()) {
            val connectManager =
                context.getSystemService(Context.CONNECTIVITY_SERVICE) as? ConnectivityManager
                    ?: return
            connectManager.registerNetworkCallback(request, this)
            mIsCallbackRegistered = true
        }
        mObservers.add(observer)
    }

    fun unregisterObserver(context: Context, observer: INetworkStateChangedObserver) {
        if (!mObservers.contains(observer)) {
            return
        }
        mObservers.remove(observer)
        if (mObservers.isEmpty()) {
            val connectManager =
                context.getSystemService(Context.CONNECTIVITY_SERVICE) as? ConnectivityManager
                    ?: return
            connectManager.unregisterNetworkCallback(this)
            mIsCallbackRegistered = false
        }
    }

    private fun notifyObserver(available: Boolean) {
        if (mObservers.isEmpty()) return
        mObservers.forEach {
            if (available) {
                it.onNetworkAvailable()
            } else {
                it.onNetworkUnavailable()
            }
        }
    }
}